package it.avantsoft.ls.gl.data;

import it.avantsoft.ls.base.Protocol;
import it.avantsoft.ls.gl.GroupChildHandler;
import it.avantsoft.ls.util.SimpleEntry;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.*;

public class UpperAB implements AddressBook {

    private int abID = AddressBook.UPPERAB_ID;
    private HashMap<String, Integer> map;
    private String mostLoadedPeer = null;
    private String lessLoadedPeer = null;
    private Map.Entry<String, Integer> bestGl;

    public UpperAB() {
        map = new HashMap<String, Integer>();

    }

    public boolean add(String host, int port) {
        map.put(host, port);
        return true;

    }

    public int size() {
        return map.size();  //To change body of implemented methods use File | Settings | File Templates.
    }

    public GroupChildHandler getBestPeer() {
        return null;
    }

    public byte[] toByte() {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

        for (Map.Entry<String, Integer> mapEntry : map.entrySet()) {
            byte[] objectByteArray = mapEntry.getKey().getBytes(); //trasformo la stringa in bytesbyte[]
            int objectByteArraySize = mapEntry.getKey().length();//segno la lunghezza per poter creare un nuovo byte[] in ricezione
            byteArrayOutputStream.write(objectByteArraySize);//scrivo la lunghezza
            try {
                byteArrayOutputStream.write(objectByteArray);//scrivo la tringa
            } catch (IOException e) {
                e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
            }

            byteArrayOutputStream.write(mapEntry.getValue());

            try {
                byteArrayOutputStream.flush();
            } catch (IOException e) {
                e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
            }
        }

        //byteArrayOutputStream.write(LOWERAB_ID); //scrivo l'id

        /*Set<String> s = map.keySet(); //prendo tutti gli elementi (solo la chiave)

        for (String o : s) {
            byte[] objectByteArray = o.getBytes(); //trasformo la stringa in bytes
            int objectByteArraySize = o.length();//segno la lunghezza per poter creare un nuovo byte[] in ricezione
            byteArrayOutputStream.write(objectByteArraySize);//scrivo la lunghezza

            try {
                byteArrayOutputStream.write(objectByteArray);//scrivo la tringa
            } catch (IOException e) {
                e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
            }

            try {
                byteArrayOutputStream.flush();
            } catch (IOException e) {
                e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
            }
        }*/
        return byteArrayOutputStream.toByteArray();
    }

    public boolean fromByte(byte[] byteArray) {
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArray);
        //level = byteArrayInputStream.read();
        HashMap<String, Integer> newMap = new HashMap<String, Integer>();
        while (byteArrayInputStream.available() > 0) {
            int objectByteArraySize = byteArrayInputStream.read();
            byte[] charByte = new byte[objectByteArraySize];
            int counter = 0;
            while (counter < objectByteArraySize) {

                charByte[counter] = (byte) byteArrayInputStream.read();
                counter++;
            }

            byte portSize = (byte) byteArrayInputStream.read();
            byte[] port = new byte[portSize];
            counter = 0;
            while (counter < portSize) {

                port[counter] = (byte) byteArrayInputStream.read();
                counter++;
            }

            newMap.put(new String(charByte), Integer.valueOf(new String(port)));
        }
        map = newMap;
        return true;
    }

    public boolean remove(String childId) {
        map.remove(childId);
        return true;
    }

    public int getAbID() {
        return abID;  //To change body of implemented methods use File | Settings | File Templates.
    }

    public boolean mergeAB(AddressBook newAddressBook) {

        boolean merged = false;
        if (size() == 0) {
            this.map = ((UpperAB) newAddressBook).getMap();
            merged = true;
        }

        return merged;
    }

    public boolean mergeAB(CenterAB newAddressBook) {

        boolean merged = false;
        if (size() == 0) {
            this.map = newAddressBook.getMap();
            merged = true;
        }

        return merged;
    }

    public HashMap<String, Integer> getMap() {
        return map;  //To change body of implemented methods use File | Settings | File Templates.
    }

    public GroupChildHandler getChildHandler(String childId) {
        return null;  //To change body of implemented methods use File | Settings | File Templates.
    }

    public boolean hasPeerEntry(String childId) {
        return false;
    }

    public Protocol.ABProtocol updateAppraisals(Appraisals appraisals) {
        return null;  //To change body of implemented methods use File | Settings | File Templates.
    }

    public Collection<Integer> getAll() {
        return map.values();  //To change body of implemented methods use File | Settings | File Templates.
    }

    public Collection<Integer> getAll(String excludePeer) {

        HashMap<String, Integer> newMap;
        newMap = new LinkedHashMap<String, Integer>();

        newMap.putAll(map);

        newMap.remove(excludePeer);

        return newMap.values();

    }

    public boolean add(GroupChildHandler child) {
        return false;  //To change body of implemented methods use File | Settings | File Templates.
    }

    public boolean add(String id) {

        boolean added = false;

        if (map.put(id, null) == null)
            added = true;

        return added;
    }

    public boolean peerKnown(String peerId) {
        return map.containsKey(peerId);
    }

    public String toString() {
        return "UpperAB{" +
                "map=" + map +
                ", abID=" + abID +
                '}';
    }

    public Map.Entry<String, Integer> getGL() {
        Random r = new Random();
        String o = (String) map.keySet().toArray()[r.nextInt(map.size())];

        return new SimpleEntry<String, Integer>(o, map.get(o));
    }
}
